#!/usr/bin/perl

#
# amarokSyndication - Amarok script for syndicating (atom file format) your music to Google Pages
#
# $Id: AtomSyndication.pm 2008-10-05 19:18:11Z dpino$
#
# (C) 2008  Diego Pino  <pinowsky at gmail com>
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
#

package AtomSyndication;

use HTTP::Cookies;
use LWP::UserAgent;
use File::Find;
use Data::Dumper;

    # Need the absolute path when is lunched from Amarok
use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/";
use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/lib/";
use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/modules/";
use lib "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/prefs/";

require "atom.pl";
require "util.pl";

@firefox_like_headers = (
        'User-Agent' => '/Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)',
        'Accept' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
        'Accept-Language' => 'en-us,en;q=0.5',
        'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
        'Keep-Alive' => 300,
        'Connection:' => 'keep-alive'
);

    # Read config 
if (-e "./.conf.rc") {
    $conf_obj = new Config::General("./.conf.rc");

    %config = $conf_obj->getall;
    $username = $config{"username"};
} 

    # Read preferences
if (-e "$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/prefs/preferences.rc") {
    # &debug("Reading preferences");
    $prefs_obj = new Config::General("$ENV{'HOME'}/.kde/share/apps/amarok/scripts/atomSyndication/prefs/preferences.rc");

    %preferences = $prefs_obj->getall;
    $VERSION = $preferences{"VERSION"};
    $APP_NAME = $preferences{"APP_NAME"};
    $PLUGIN_NAME = $preferences{"PLUGIN_NAME"};
    $ATOM_FILE =  $preferences{"ATOM_FILE"};
    $MAX_ELEMENTS = $preferences{"MAX_ELEMENTS"};
    $debug = $preferences{"debug"};
}

sub new
{
    local($this, $username, $password, $use_firefox) = @_;

        # Search for cookies.txt file under users firefox home folder
    find({wanted => sub { if (/cookies\.txt/) { $File::Find::result = $File::Find::name; }  }}, "$ENV{'HOME'}/.mozilla/firefox/");
    my $cookiesFile = ($File::Find::result ne "") ? $File::Find::result : die ("Could not find cookies.txt file under $firefox directory\n");

        # Prepare cookie_jar
    my $cookie_jar = HTTP::Cookies::Netscape->new(
            file     => $cookiesFile,
            autosave => 1,
            ignore_discard => 0,
            hide_cookie2 => 1
    );

        # Prepare UserAgent
    my $ua;
    $ua = LWP::UserAgent->new();
    $ua->cookie_jar($cookie_jar);

        # Start debugging
    open DEBUG, ">/tmp/atomSyndication.log";

        # Bless object and return
    my $atomSyndication = {
        "ua" => $ua,
        "username" => $username,
        "password" => $password,
        "use_firefox" => $use_firefox,
    };
    bless $atomSyndication, "AtomSyndication";

    $URL_ATOM_FILE = "http://$username.googlepages.com/$ATOM_FILE";
    $UPLOAD_URL = "http://pages.google.com/upload-file-simple/$username";

    return $atomSyndication;
}

##
# Get PAGES_SID from sessionstore.js Firefox file
#
sub getPagesSID()
{
        # Search for sessionstore.js file under users firefox home folder
    my $sessionStoreFile;

    find({wanted => sub { if (/sessionstore\.js/) { $sessionStoreFile = $File::Find::name; }  }}, "$ENV{'HOME'}/.mozilla/firefox/");
    if ($DEBUG) { 
        &debug("sessionStoreFile: $sessionStoreFile"); }

        # Open sessionstore.js and fetch it all into lines
    open SESSION_STORE, "<$sessionStoreFile";
    $content = <SESSION_STORE>;
    @lines = split(',', $content);

        # Go through each line and search for PAGES_SID value
    foreach $line (@lines) {
        ($PAGES_SID) = $line =~ m/$\s*value[0-9]+:\"PAGES_SID=(.*);domain=pages.google.com/;

        if ($PAGES_SID ne "") {
            return $PAGES_SID;
        }
    }

    return "";
}


##
# Retrieves PAGES_SID by google standard authentication process
#
# This procedure is extensively documented at http://code.google.com/apis/accounts/AuthForInstalledApps.html
#
sub authenticate()
{
	local($this) = shift;

	my $url = 'https://www.google.com/accounts/ClientLogin';
	my $response = $this->{ua}->post($url, {
            Email => $this->{username},
            Passwd => $this->{password}, 
            service => 'pages', 
            source => "$PLUGIN_NAME-$APP_NAME-$VERSION"
        });

	# Extract the Authentication
	if ($response->is_success) {
		my($auth) = $response->content =~ /Auth=(.*?)\n/;
		if ($auth ne "") {
                        print "auth: $auth"."\n";
			return $auth;
		}
	} 

	return null;
}


##
# Return authtoken string for uploading
#
# If password is set, tries to log to Google Pages via ClientLogin authentication mechanism. 
# 
# If password is not set, then tries to fetch PAGES_SID (Google Pages Session ID) via Firefox. For doing it so, you should first have started a session to Google Pages on your Firefox browser, and keep that session open. That means, log on to Google Pages and DO NOT close the main page. Your PAGES_SID token will be stores on sessionstore.js file. The script locates and reads this file to fetch the Pages SID.
# 
sub getAuthToken()
{
    local($this) = shift;

        # Prepare cookie_jar
    my $cookie_jar = HTTP::Cookies::Netscape->new(
        file     => $cookiesFile,
        autosave => 1,
        ignore_discard => 0,
        hide_cookie2 => 1
    );

        # Get PAGES_SID using current Firefox opened session
    if ($this->{use_firefox}) {
        $PAGES_SID = &getPagesSID();
    }
    &debug("Firefox Session->PAGES_SID: $PAGES_SID\n");

    if ($PAGES_SID eq "") {

            # Get PAGES_SID by logging at Google Pages via ClientLogin
        if ($this->{username} ne "" && $this->{password} ne "") {
            $PAGES_SID = $this->authenticate();
            &debug("ClientLogin->PAGES_SID: $PAGES_SID\n");
            # print "PAGES_SID: $PAGES_SID\n";
        }
    }

    if ($PAGES_SID eq "") {
        return "";
    }

    $cookie_jar->set_cookie(0, "PAGES_SID", $PAGES_SID, "/", ".google.com", $port, 0, '', 7200, 0);
    $this->{ua}->cookie_jar($cookie_jar);

        # Fetch upload_authtoken string from URL
    local($response) = $this->{ua}->get("http://pages.google.com/?pli=1", @firefox_like_headers);
    local($upload_authtoken) = $response->content =~ m/upload: ".*?authtoken=(.*)"/;

    if ($DEBUG) { 
        &debug("response: " . Dumper($response));
        &debug("upload_authtoken: $upload_authtoken");
    } 

    return $upload_authtoken;
}

##
# Uploads a file, in case a file with the same name already exists, upload overwrites existing file
#
sub uploadFile()
{
	local($this, $filename, $authtoken) = @_;

	if ($DEBUG) {
		&debug("onUploadFile");
		&debug("UPLOAD_URL: $UPLOAD_URL");
		&debug("filename: $filename");
		&debug("authtoken: $authtoken");
	}

	$response = $this->{ua}->post("$UPLOAD_URL/?authtoken=$authtoken",
			['file' => [$filename]],
			Content_Type => 'form-data',
			@firefox_like_headers
		);

	&debug($response->content);
}

sub run
{
    local($this, $authtoken) = @_;

#--------------------------------------------------
#     &debug("Version: $VERSION");
#     &debug("App: $APP_NAME");
#     &debug("Plugin name: $PLUGIN_NAME");
#     &debug("Atom file: $ATOM_FILE");
#     &debug("Max elements: $MAX_ELEMENTS");
#     &debug("Debug: $debug");
# 
#     &debug("URL_ATOM_FILE: $URL_ATOM_FILE");
#     &debug("UPLOAD_URL: http://pages.google.com/upload-file-simple/$username");
#-------------------------------------------------- 


        # Fetch Atom file from URL
    $xmldoc = &GetXML($URL_ATOM_FILE);
    &debug("URL_ATOM_FILE: $URL_ATOM_FILE");
    print DEBUG "xmldoc: " . Dumper($xmldoc);
    if ($xmldoc == undef) {
        &debug("Create new XML file");
        $xmldoc = &CreateXML();
    }

    while (1) {
            $_ = <STDIN>;
            chomp($_);
            if (/trackChange/) {
               &debug("track changed");
               &UpdateXML($xmldoc); 
               &SaveXML($xmldoc, "./$ATOM_FILE") || die("Error on saving\n");
               $this->uploadFile($ATOM_FILE, $authtoken) || die("Error on uploading\n");
            }
    }
}

1;
